using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using System.Reflection;
using System.IO;
using System.Threading;
using System.Drawing;

namespace Lizk.WebScraper
{
    public abstract class Scraper
    {
        public abstract ScraperResult[] Search(string search);
        public abstract ScraperResult GetTitleInfo(string id);
        public event EventHandler<ScraperEventArgs> ProgressUpdated;
        public event EventHandler<ScrapeEndResultArgs> ScapeCompleted;
        public event EventHandler<ScaperSearchArgs> SearchCompleted;
        private object tag;
        private Thread asyncThread;
        public object Tag
        {
            get { return tag; }
            set { tag = value; }
        }

        protected CookieContainer cookies = new CookieContainer();

        public static string Scrape(int i, string s, string start, string end)
        {
            int hat;
            return Scrape(i, s, start, end, out hat);
        }
        public static string Scrape(int i, string s, string start, string end, out int endIndex)
        {
            try
            {
                int b = s.IndexOf(start, i) + start.Length;
                endIndex = s.IndexOf(end, b);
                return s.Substring(b, endIndex - b);
            }
            catch
            {
                endIndex = 0;
                return "";
            }
        }

        protected void UpdateProgress(ScraperEventArgs eventArgs)
        {
            if (ProgressUpdated == null)
                return;

            ProgressUpdated(this, eventArgs);
        }

        protected HttpWebResponse Connect(Uri uri)
        {
            return Connect(uri, true);
        }

        protected HttpWebResponse Connect(Uri uri, bool useCookies)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            if (useCookies)
                request.CookieContainer = cookies;

            return (HttpWebResponse)request.GetResponse();
        }

        protected bool TryGetPage(string url, out string text)
        {
            try
            {
                Uri i = new Uri(url);
                using (StreamReader s = new StreamReader(Connect(i).GetResponseStream()))
                {
                    text = s.ReadToEnd();
                }
                return true;
            }
            catch
            {
                text = null;
                return false;
            }
        }

        public void BeginSearch(string searchString)
        {
            if (asyncThread != null && asyncThread.ThreadState == ThreadState.Running)
            {
                asyncThread.Abort();
                asyncThread = null;
            }
            asyncThread = new Thread(new ParameterizedThreadStart(AsyncSearch));
            asyncThread.IsBackground = true;
            asyncThread.Start(new KeyValuePair<Scraper, string>(this, searchString));
        }

        public void StopAsyncScrape()
        {
            if (asyncThread != null && asyncThread.ThreadState == ThreadState.Running)
            {
                asyncThread.Abort();
                asyncThread = null;
            }
        }

        private static void AsyncSearch(object o)
        {
            KeyValuePair<Scraper, string> s = (KeyValuePair<Scraper, string>)o;
            //try
            //{
                s.Key.Search(s.Value);
/*            }
            catch (Exception e)
            {
                s.Key.UpdateProgress(new ScraperEventArgs("Error: " + e.Message, ScraperEventArgs.ScaperStatus.Error));
           }*/
        }

        public void BeginGetInfo(string id)
        {
            if (asyncThread != null && asyncThread.ThreadState == ThreadState.Running)
            {
                asyncThread.Abort();
                asyncThread = null;
            }
            asyncThread = new Thread(new ParameterizedThreadStart(AsyncGetInfo));
            asyncThread.IsBackground = true;
            asyncThread.Start(new KeyValuePair<Scraper, string>(this, id));
        }

        private static void AsyncGetInfo(object o)
        {
            KeyValuePair<Scraper, string> s = (KeyValuePair<Scraper, string>)o;
            s.Key.GetTitleInfo(s.Value);
        }

        public bool TryDownloadImage(string url, out string tmpImagePath)
        {
            Uri i = new Uri(url);
            try {
                using (Image image = Image.FromStream(Connect(i).GetResponseStream()))
                {
                    tmpImagePath = Path.GetTempFileName();
                    image.Save(tmpImagePath, System.Drawing.Imaging.ImageFormat.Png);
                }
                return true;
            } catch {
                tmpImagePath = null;
                return false;
            }
        }

        public static string br2nl(string s)
        {
            return Regex.Replace(s, @"<br[^\w]*>", Environment.NewLine);
        }

        public static string ClearHTML(string s)
        {
            return Regex.Replace(s, @"<[^>]*>", string.Empty);
        }

        public static void LoadPluginAssembly(Assembly ass)
        {
            foreach (Type t in ass.GetTypes())
            {
                if (t.IsSubclassOf(typeof(ScraperResult)))
                {
                    if (!resultTypes.Contains(t))
                    {
                        resultTypes.Add(t);
                    }
                }
                if (t.IsSubclassOf(typeof(Scraper)))
                {
                    if (!scrapers.Contains(t))
                    {
                        scrapers.Add(t);
                        ConstructorInfo ci = t.GetConstructor(Type.EmptyTypes);
                        ci.Invoke(null);
                    }
                }
            }
        }

        private static List<Type> scrapers = new List<Type>();

        public static List<Type> Scrapers
        {
            get { return scrapers; }
        }

        private static List<Type> resultTypes = new List<Type>();

	    public static List<Type> ResultTypes
	    {
		    get { return resultTypes;}
	    }
        static Scraper() 
        {
            resultTypes.Add(typeof(ScraperResult));
        }

        protected void ScrapeComplete(ScraperResult result)
        {
            if(ScapeCompleted != null)
                ScapeCompleted(this, new ScrapeEndResultArgs( result));
        }

        protected void SearchComplete(IEnumerable<ScraperResult> results)
        {
            if (SearchCompleted != null)
                SearchCompleted(this, new ScaperSearchArgs(results));
        }

        public static Scraper GetScraperInstance(string type)
        {
            foreach (Type t in Scrapers)
            {
                if (t.FullName.Contains(type))
                    return (Scraper)t.GetConstructor(Type.EmptyTypes).Invoke(null);
            }
            return null;
        }
        
    }

    public class ScaperSearchArgs : EventArgs
    {
        private IEnumerable<ScraperResult> itemsFound;

        public IEnumerable<ScraperResult> ResultsFound
        {
            get { return itemsFound; }
            set { itemsFound = value; }
        }
        public ScaperSearchArgs() { }
        public ScaperSearchArgs(IEnumerable<ScraperResult> results) { this.itemsFound = results; }

    }

    public class ScrapeEndResultArgs : EventArgs
    {
        private ScraperResult result;

        public ScraperResult Result
        {
            get { return result; }
            set { result = value; }
        }
        public ScrapeEndResultArgs() { }
        public ScrapeEndResultArgs(ScraperResult results) { this.result = results; }
    }


    public class ScraperEventArgs : EventArgs
    {
        private string message = "";
        private ScaperStatus status = ScaperStatus.Idle;
        private int percentComplete = -1;



        public int PercentComplete
        {
            get { return percentComplete; }
            set { percentComplete = value; }
        }

        public ScaperStatus Status
        {
            get { return status; }
            set { status = value; }
        }
	
        public string Message
        {
            get { return message; }
            set { message = value; }
        }

        public ScraperEventArgs(string message, ScaperStatus status)
        {
            this.status = status;
            this.message = message;

        }


        public ScraperEventArgs(string message, ScaperStatus status, int percentComplete)
        {
            this.message = message;
            this.status = status;
            this.percentComplete = percentComplete;
        }

        public ScraperEventArgs() { }

        public enum ScaperStatus
        {
            Idle,
            Connecting,
            Downloading,
            Done,
            Error
        }
    }

}
